﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.Metadata.Edm;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using Vaffanculo.Data.Interfaces;
using System.Data.Entity;

namespace Vaffanculo.Data.Implements
{
    /// <summary>
    /// Generic Repository for entities to database.
    /// </summary> 
    /// <typeparam name="TEntity">
    /// The entity type that requires a Repository.
    /// </typeparam>
    public class Repository<TEntity> : IRepository<TEntity>, IDisposable
        where TEntity : class
    {
        private readonly IDbContext _ctx;
        private IDbSet<TEntity> _dbSet;
        
        public Repository(IUnitOfWork uow)
        {
            _dbSet = uow.GetIDbContext().Set<TEntity>();
        }


        #region IRepository<TEntity> Members

        public int Save()
        {
            return _ctx.SaveChanges();
        }            

        /// <summary>
        /// Insert new data into context
        /// </summary>
        /// <typeparam name="Entity">
        /// The entity type that requires a Repository.
        /// </typeparam>
        public void Add(TEntity entity)
        {
            _dbSet.Add(entity);
        }

        /// <summary>
        /// Delete data from context
        /// </summary>
        /// <typeparam name="Entity">
        /// The entity type that requires a Repository.
        /// </typeparam>
        public void Delete(TEntity entity)
        {
            _dbSet.Attach(entity);
            _dbSet.Remove(entity);            
        }
        
        /// <summary>
        /// Delete Entity by specfied criteria
        /// </summary>
        /// <param name="filter">LinQ expression</param>
        public void DeleteByFilter(Expression<Func<TEntity, bool>> filter)
        {
            TEntity entity = this.GetSingle(filter);
            _dbSet.Remove(entity);
        }

        /// <summary>
        /// Get Count of all records
        /// </summary>
        /// <returns>int</returns>
        public int GetCount()
        {
            return _dbSet.Count<TEntity>();
        }

        /// <summary>
        /// Get all records
        /// </summary>
        /// <returns>IList<TEntity></returns>
        public IList<TEntity> GetAll()
        {
            return _dbSet.ToList();
        }

        /// <summary>
        /// Get all records filter for criteria
        /// </summary>
        /// <returns>IList<TEntity></returns>
        public IList<TEntity> GetByFilter(Expression<Func<TEntity, bool>> filter)
        {
            return _dbSet.Where(filter).ToList();
        }

        /// <summary>
        /// Get all records as an IQueryable
        /// </summary>
        /// <returns>IQueryable<TEntity></returns>
        public IQueryable<TEntity> AsQueryable()
        {
            return _dbSet.AsQueryable();
        }

        /// <summary>
        /// Get records as an IQueryable by filter
        /// </summary>
        /// <returns>IQueryable<TEntity></returns>
        public IQueryable<TEntity> AsQueryableByFilter(Expression<Func<TEntity, bool>> filter)
        {
            return _dbSet.AsQueryable().Where(filter);
        }

        /// <summary>
        /// Get records as an IEnumerable
        /// </summary>
        /// <returns>IEnumerable<TEntity></returns>
        public IEnumerable<TEntity> GetAllIEnumerable()
        {
            return this.AsQueryable().AsEnumerable();
        }

        /// <summary>
        /// Get records as an IEnumerable by filter
        /// </summary>
        /// <returns>IEnumerable<TEntity></returns>
        public IEnumerable<TEntity> GetIEnumerableByFilter(Expression<Func<TEntity, bool>> filter)
        {
            return this.AsQueryableByFilter(filter).AsEnumerable();
        }
        
        /// <summary>
        /// Get a single Entity by specified filter (criteria)
        /// </summary>
        /// <returns>Entity</returns>
        public TEntity GetSingle(Expression<Func<TEntity, bool>> filter)
        {
            return _dbSet.Single<TEntity>(filter);
        }

        /// <summary>
        /// Get First Entity by filter
        /// </summary>
        /// <returns>Entity</returns>
        public TEntity GetFirst(Expression<Func<TEntity, bool>> filter)
        {
            return _dbSet.First<TEntity>(filter);
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            if (null != _ctx)
            {
                _ctx.Dispose();
            }
        }

        #endregion

    }
}
